﻿namespace Spectral
{
    using System;
    using Spectral.Operations;

    /// <summary>
    /// Represents a 2 dimensional vector.
    /// </summary>
    /// <typeparam name="T">The element type.</typeparam>
    public struct Vector2<T> : IEquatable<Vector2<T>>
        where T : struct, IEquatable<T>, IComparable<T>
    {
        /// <summary>
        /// A vector with all elements set to zero.
        /// </summary>
        public static readonly Vector2<T> Zero = new Vector2<T>(Operator.Zero<T>());

        /// <summary>
        /// A vector with all elements set to one.
        /// </summary>
        public static readonly Vector2<T> One = new Vector2<T>(Operator.One<T>());

        /// <summary>
        /// A vector with the x element set to one.
        /// </summary>
        public static readonly Vector2<T> UnitX = new Vector2<T>(Operator.One<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the y element set to one.
        /// </summary>
        public static readonly Vector2<T> UnitY = new Vector2<T>(Operator.Zero<T>(), Operator.One<T>());
        
        /// <summary>
        /// The x component.
        /// </summary>
        public readonly T X;
        
        /// <summary>
        /// The y component.
        /// </summary>
        public readonly T Y;
        
        /// <summary>
        /// Gets the element at the specified index.
        /// </summary>
        public T this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return X;
                    case 1: return Y;
                    default: throw new IndexOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector2&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="value">The value.</param>
        public Vector2(T value)
        {
            X = value;
            Y = value;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector2&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="x">The x element.</param>
        /// <param name="y">The y element.</param>
        public Vector2(T x, T y)
        {
            X = x;
            Y = y;
        }

        /// <summary>
        /// Adds the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Add(Vector2<T> value)
        {
            return new Vector2<T>(X.Add(value.X), Y.Add(value.Y));
        }

        /// <summary>
        /// Subtracts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Subtract(Vector2<T> value)
        {
            return new Vector2<T>(X.Subtract(value.X), Y.Subtract(value.Y));
        }

        /// <summary>
        /// Multiplies by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Multiply(T value)
        {
            return new Vector2<T>(X.Multiply(value), Y.Multiply(value));
        }

        /// <summary>
        /// Divides by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Divide(T value)
        {
            return new Vector2<T>(X.Divide(value), Y.Divide(value));
        }

        /// <summary>
        /// Negates this instance.
        /// </summary>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Negate()
        {
            return new Vector2<T>(X.Negate(), Y.Negate());
        }

        /// <summary>
        /// Calculates the sum of all elements in this vector.
        /// </summary>
        /// <returns>The sum of the elements.</returns>
        public T Sum()
        {
            return (X).Add(Y);
        }

        /// <summary>
        /// Casts this vector into a vector of a different vector type.
        /// </summary>
        /// <returns>A vector.</returns>
        public Vector2<U> Cast<U>()
            where U : struct, IEquatable<U>, IComparable<U>
        {
            return new Vector2<U>((U)Convert.ChangeType(X, typeof(U)), (U)Convert.ChangeType(Y, typeof(U)));
        }

        /// <summary>
        /// Creates an array containing this vector's elements.
        /// </summary>
        /// <returns>An array containing this vector's elements.</returns>
        public T[] ToArray()
        {
            return new[] { X, Y };
        }

        /// <summary>
        /// Calculates the length of the vector.
        /// </summary>
        /// <returns>The length of the vector.</returns>
        public T Length()
        {
            return LengthSquared().Sqrt();
        }

        /// <summary>
        /// Calculates the squared length of the vector.
        /// </summary>
        /// <returns>The squared length of the vector.</returns>
        public T LengthSquared()
        {
            return (X.Multiply(X)).Add(Y.Multiply(Y));
        }

        /// <summary>
        /// Calculates the distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The distance to the vector.</returns>
        public T Distance(Vector2<T> value)
        {
            return DistanceSquared(value).Sqrt();
        }

        /// <summary>
        /// Calculates the squared distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The squared distance to the vector.</returns>
        public T DistanceSquared(Vector2<T> value)
        {
            return Subtract(value).LengthSquared();
        }

        /// <summary>
        /// Calculates the dot product between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public T Dot(Vector2<T> value)
        {
            return (X.Multiply(value.X)).Add(Y.Multiply(value.Y));
        }
        
        /// <summary>
        /// Calculates a vector with the minimum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Min(Vector2<T> value)
        {
            return new Vector2<T>(X.CompareTo(value.X) < 0 ? X : value.X, Y.CompareTo(value.Y) < 0 ? Y : value.Y);
        }

        /// <summary>
        /// Calculates a vector with the maximum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Max(Vector2<T> value)
        {
            return new Vector2<T>(X.CompareTo(value.X) > 0 ? X : value.X, Y.CompareTo(value.Y) > 0 ? Y : value.Y);
        }

        /// <summary>
        /// Clamps this vector between two values.
        /// </summary>
        /// <param name="min">The minimum value.</param>
        /// <param name="max">The maximum value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector2<T> Clamp(Vector2<T> min, Vector2<T> max)
        {
            return Min(max).Max(min);
        }
        
        /// <summary>
        /// Transforms a vector by a matrix.
        /// </summary>
        /// <param name="value">The transformation matrix.</param>
        /// <returns>The result of the operation</returns>
        public Vector2<T> Transform(Matrix2x2<T> value)
        {
            return new Vector2<T>((X.Multiply(value.M11)).Add(Y.Multiply(value.M21)), (X.Multiply(value.M12)).Add(Y.Multiply(value.M22)));
        }
        
        /// <summary>
        /// Transforms a point by a matrix.
        /// </summary>
        /// <param name="value">The transformation matrix.</param>
        /// <returns>The result of the operation</returns>
        public Vector2<T> TransformCoordinate(Matrix3x3<T> value)
        {
            return new Vector2<T>((X.Multiply(value.M11)).Add(Y.Multiply(value.M12)).Add(value.M13), (X.Multiply(value.M21)).Add(Y.Multiply(value.M22)).Add(value.M23)).Divide((value.M31).Add(value.M32).Add(value.M33));
        }
        
        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector2<T> operator +(Vector2<T> left, Vector2<T> right)
        {
            return left.Add(right);
        }

        /// <summary>
        /// Subtracts one vector from another.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector2<T> operator -(Vector2<T> left, Vector2<T> right)
        {
            return left.Subtract(right);
        }

        /// <summary>
        /// Multiplies a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector2<T> operator *(Vector2<T> left, T right)
        {
            return left.Multiply(right);
        }

        /// <summary>
        /// Divides a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector2<T> operator /(Vector2<T> left, T right)
        {
            return left.Divide(right);
        }

        /// <summary>
        /// Negates a vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation</returns>
        public static Vector2<T> operator -(Vector2<T> value)
        {
            return value;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Vector2<T> left, Vector2<T> right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is not equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Vector2<T> left, Vector2<T> right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Vector2<T> other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            return obj is Vector2<T> ? Equals((Vector2<T>)obj) : false;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return "X: " + X.ToString() + ", " + "Y: " + Y.ToString();
        }
    }

    /// <summary>
    /// Represents a 3 dimensional vector.
    /// </summary>
    /// <typeparam name="T">The element type.</typeparam>
    public struct Vector3<T> : IEquatable<Vector3<T>>
        where T : struct, IEquatable<T>, IComparable<T>
    {
        /// <summary>
        /// A vector with all elements set to zero.
        /// </summary>
        public static readonly Vector3<T> Zero = new Vector3<T>(Operator.Zero<T>());

        /// <summary>
        /// A vector with all elements set to one.
        /// </summary>
        public static readonly Vector3<T> One = new Vector3<T>(Operator.One<T>());

        /// <summary>
        /// A vector with the x element set to one.
        /// </summary>
        public static readonly Vector3<T> UnitX = new Vector3<T>(Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the y element set to one.
        /// </summary>
        public static readonly Vector3<T> UnitY = new Vector3<T>(Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the z element set to one.
        /// </summary>
        public static readonly Vector3<T> UnitZ = new Vector3<T>(Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>());
        
        /// <summary>
        /// The x component.
        /// </summary>
        public readonly T X;
        
        /// <summary>
        /// The y component.
        /// </summary>
        public readonly T Y;
        
        /// <summary>
        /// The z component.
        /// </summary>
        public readonly T Z;
        
        /// <summary>
        /// Gets the element at the specified index.
        /// </summary>
        public T this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return X;
                    case 1: return Y;
                    case 2: return Z;
                    default: throw new IndexOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector3&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="value">The value.</param>
        public Vector3(T value)
        {
            X = value;
            Y = value;
            Z = value;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector3&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="x">The x element.</param>
        /// <param name="y">The y element.</param>
        /// <param name="z">The z element.</param>
        public Vector3(T x, T y, T z)
        {
            X = x;
            Y = y;
            Z = z;
        }

        /// <summary>
        /// Adds the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Add(Vector3<T> value)
        {
            return new Vector3<T>(X.Add(value.X), Y.Add(value.Y), Z.Add(value.Z));
        }

        /// <summary>
        /// Subtracts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Subtract(Vector3<T> value)
        {
            return new Vector3<T>(X.Subtract(value.X), Y.Subtract(value.Y), Z.Subtract(value.Z));
        }

        /// <summary>
        /// Multiplies by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Multiply(T value)
        {
            return new Vector3<T>(X.Multiply(value), Y.Multiply(value), Z.Multiply(value));
        }

        /// <summary>
        /// Divides by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Divide(T value)
        {
            return new Vector3<T>(X.Divide(value), Y.Divide(value), Z.Divide(value));
        }

        /// <summary>
        /// Negates this instance.
        /// </summary>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Negate()
        {
            return new Vector3<T>(X.Negate(), Y.Negate(), Z.Negate());
        }

        /// <summary>
        /// Calculates the sum of all elements in this vector.
        /// </summary>
        /// <returns>The sum of the elements.</returns>
        public T Sum()
        {
            return (X).Add(Y).Add(Z);
        }

        /// <summary>
        /// Casts this vector into a vector of a different vector type.
        /// </summary>
        /// <returns>A vector.</returns>
        public Vector3<U> Cast<U>()
            where U : struct, IEquatable<U>, IComparable<U>
        {
            return new Vector3<U>((U)Convert.ChangeType(X, typeof(U)), (U)Convert.ChangeType(Y, typeof(U)), (U)Convert.ChangeType(Z, typeof(U)));
        }

        /// <summary>
        /// Creates an array containing this vector's elements.
        /// </summary>
        /// <returns>An array containing this vector's elements.</returns>
        public T[] ToArray()
        {
            return new[] { X, Y, Z };
        }

        /// <summary>
        /// Calculates the length of the vector.
        /// </summary>
        /// <returns>The length of the vector.</returns>
        public T Length()
        {
            return LengthSquared().Sqrt();
        }

        /// <summary>
        /// Calculates the squared length of the vector.
        /// </summary>
        /// <returns>The squared length of the vector.</returns>
        public T LengthSquared()
        {
            return (X.Multiply(X)).Add(Y.Multiply(Y)).Add(Z.Multiply(Z));
        }

        /// <summary>
        /// Calculates the distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The distance to the vector.</returns>
        public T Distance(Vector3<T> value)
        {
            return DistanceSquared(value).Sqrt();
        }

        /// <summary>
        /// Calculates the squared distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The squared distance to the vector.</returns>
        public T DistanceSquared(Vector3<T> value)
        {
            return Subtract(value).LengthSquared();
        }

        /// <summary>
        /// Calculates the dot product between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public T Dot(Vector3<T> value)
        {
            return (X.Multiply(value.X)).Add(Y.Multiply(value.Y)).Add(Z.Multiply(value.Z));
        }
        
        /// <summary>
        /// Calculates a vector with the minimum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Min(Vector3<T> value)
        {
            return new Vector3<T>(X.CompareTo(value.X) < 0 ? X : value.X, Y.CompareTo(value.Y) < 0 ? Y : value.Y, Z.CompareTo(value.Z) < 0 ? Z : value.Z);
        }

        /// <summary>
        /// Calculates a vector with the maximum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Max(Vector3<T> value)
        {
            return new Vector3<T>(X.CompareTo(value.X) > 0 ? X : value.X, Y.CompareTo(value.Y) > 0 ? Y : value.Y, Z.CompareTo(value.Z) > 0 ? Z : value.Z);
        }

        /// <summary>
        /// Clamps this vector between two values.
        /// </summary>
        /// <param name="min">The minimum value.</param>
        /// <param name="max">The maximum value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector3<T> Clamp(Vector3<T> min, Vector3<T> max)
        {
            return Min(max).Max(min);
        }
        
        /// <summary>
        /// Transforms a vector by a matrix.
        /// </summary>
        /// <param name="value">The transformation matrix.</param>
        /// <returns>The result of the operation</returns>
        public Vector3<T> Transform(Matrix3x3<T> value)
        {
            return new Vector3<T>((X.Multiply(value.M11)).Add(Y.Multiply(value.M21)).Add(Z.Multiply(value.M31)), (X.Multiply(value.M12)).Add(Y.Multiply(value.M22)).Add(Z.Multiply(value.M32)), (X.Multiply(value.M13)).Add(Y.Multiply(value.M23)).Add(Z.Multiply(value.M33)));
        }
        
        /// <summary>
        /// Transforms a point by a matrix.
        /// </summary>
        /// <param name="value">The transformation matrix.</param>
        /// <returns>The result of the operation</returns>
        public Vector3<T> TransformCoordinate(Matrix4x4<T> value)
        {
            return new Vector3<T>((X.Multiply(value.M11)).Add(Y.Multiply(value.M12)).Add(Z.Multiply(value.M13)).Add(value.M14), (X.Multiply(value.M21)).Add(Y.Multiply(value.M22)).Add(Z.Multiply(value.M23)).Add(value.M24), (X.Multiply(value.M31)).Add(Y.Multiply(value.M32)).Add(Z.Multiply(value.M33)).Add(value.M34)).Divide((value.M41).Add(value.M42).Add(value.M43).Add(value.M44));
        }
        
        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector3<T> operator +(Vector3<T> left, Vector3<T> right)
        {
            return left.Add(right);
        }

        /// <summary>
        /// Subtracts one vector from another.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector3<T> operator -(Vector3<T> left, Vector3<T> right)
        {
            return left.Subtract(right);
        }

        /// <summary>
        /// Multiplies a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector3<T> operator *(Vector3<T> left, T right)
        {
            return left.Multiply(right);
        }

        /// <summary>
        /// Divides a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector3<T> operator /(Vector3<T> left, T right)
        {
            return left.Divide(right);
        }

        /// <summary>
        /// Negates a vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation</returns>
        public static Vector3<T> operator -(Vector3<T> value)
        {
            return value;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Vector3<T> left, Vector3<T> right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is not equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Vector3<T> left, Vector3<T> right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Vector3<T> other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            return obj is Vector3<T> ? Equals((Vector3<T>)obj) : false;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return "X: " + X.ToString() + ", " + "Y: " + Y.ToString() + ", " + "Z: " + Z.ToString();
        }
    }

    /// <summary>
    /// Represents a 4 dimensional vector.
    /// </summary>
    /// <typeparam name="T">The element type.</typeparam>
    public struct Vector4<T> : IEquatable<Vector4<T>>
        where T : struct, IEquatable<T>, IComparable<T>
    {
        /// <summary>
        /// A vector with all elements set to zero.
        /// </summary>
        public static readonly Vector4<T> Zero = new Vector4<T>(Operator.Zero<T>());

        /// <summary>
        /// A vector with all elements set to one.
        /// </summary>
        public static readonly Vector4<T> One = new Vector4<T>(Operator.One<T>());

        /// <summary>
        /// A vector with the x element set to one.
        /// </summary>
        public static readonly Vector4<T> UnitX = new Vector4<T>(Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the y element set to one.
        /// </summary>
        public static readonly Vector4<T> UnitY = new Vector4<T>(Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the z element set to one.
        /// </summary>
        public static readonly Vector4<T> UnitZ = new Vector4<T>(Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>(), Operator.Zero<T>());
        
        /// <summary>
        /// A vector with the w element set to one.
        /// </summary>
        public static readonly Vector4<T> UnitW = new Vector4<T>(Operator.Zero<T>(), Operator.Zero<T>(), Operator.Zero<T>(), Operator.One<T>());
        
        /// <summary>
        /// The x component.
        /// </summary>
        public readonly T X;
        
        /// <summary>
        /// The y component.
        /// </summary>
        public readonly T Y;
        
        /// <summary>
        /// The z component.
        /// </summary>
        public readonly T Z;
        
        /// <summary>
        /// The w component.
        /// </summary>
        public readonly T W;
        
        /// <summary>
        /// Gets the element at the specified index.
        /// </summary>
        public T this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0: return X;
                    case 1: return Y;
                    case 2: return Z;
                    case 3: return W;
                    default: throw new IndexOutOfRangeException();
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector4&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="value">The value.</param>
        public Vector4(T value)
        {
            X = value;
            Y = value;
            Z = value;
            W = value;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector4&lt;T&gt;"/> struct.
        /// </summary>
        /// <param name="x">The x element.</param>
        /// <param name="y">The y element.</param>
        /// <param name="z">The z element.</param>
        /// <param name="w">The w element.</param>
        public Vector4(T x, T y, T z, T w)
        {
            X = x;
            Y = y;
            Z = z;
            W = w;
        }

        /// <summary>
        /// Adds the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Add(Vector4<T> value)
        {
            return new Vector4<T>(X.Add(value.X), Y.Add(value.Y), Z.Add(value.Z), W.Add(value.W));
        }

        /// <summary>
        /// Subtracts the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Subtract(Vector4<T> value)
        {
            return new Vector4<T>(X.Subtract(value.X), Y.Subtract(value.Y), Z.Subtract(value.Z), W.Subtract(value.W));
        }

        /// <summary>
        /// Multiplies by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Multiply(T value)
        {
            return new Vector4<T>(X.Multiply(value), Y.Multiply(value), Z.Multiply(value), W.Multiply(value));
        }

        /// <summary>
        /// Divides by the specified value.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Divide(T value)
        {
            return new Vector4<T>(X.Divide(value), Y.Divide(value), Z.Divide(value), W.Divide(value));
        }

        /// <summary>
        /// Negates this instance.
        /// </summary>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Negate()
        {
            return new Vector4<T>(X.Negate(), Y.Negate(), Z.Negate(), W.Negate());
        }

        /// <summary>
        /// Calculates the sum of all elements in this vector.
        /// </summary>
        /// <returns>The sum of the elements.</returns>
        public T Sum()
        {
            return (X).Add(Y).Add(Z).Add(W);
        }

        /// <summary>
        /// Casts this vector into a vector of a different vector type.
        /// </summary>
        /// <returns>A vector.</returns>
        public Vector4<U> Cast<U>()
            where U : struct, IEquatable<U>, IComparable<U>
        {
            return new Vector4<U>((U)Convert.ChangeType(X, typeof(U)), (U)Convert.ChangeType(Y, typeof(U)), (U)Convert.ChangeType(Z, typeof(U)), (U)Convert.ChangeType(W, typeof(U)));
        }

        /// <summary>
        /// Creates an array containing this vector's elements.
        /// </summary>
        /// <returns>An array containing this vector's elements.</returns>
        public T[] ToArray()
        {
            return new[] { X, Y, Z, W };
        }

        /// <summary>
        /// Calculates the length of the vector.
        /// </summary>
        /// <returns>The length of the vector.</returns>
        public T Length()
        {
            return LengthSquared().Sqrt();
        }

        /// <summary>
        /// Calculates the squared length of the vector.
        /// </summary>
        /// <returns>The squared length of the vector.</returns>
        public T LengthSquared()
        {
            return (X.Multiply(X)).Add(Y.Multiply(Y)).Add(Z.Multiply(Z)).Add(W.Multiply(W));
        }

        /// <summary>
        /// Calculates the distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The distance to the vector.</returns>
        public T Distance(Vector4<T> value)
        {
            return DistanceSquared(value).Sqrt();
        }

        /// <summary>
        /// Calculates the squared distance to another vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The squared distance to the vector.</returns>
        public T DistanceSquared(Vector4<T> value)
        {
            return Subtract(value).LengthSquared();
        }

        /// <summary>
        /// Calculates the dot product between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public T Dot(Vector4<T> value)
        {
            return (X.Multiply(value.X)).Add(Y.Multiply(value.Y)).Add(Z.Multiply(value.Z)).Add(W.Multiply(value.W));
        }
        
        /// <summary>
        /// Calculates a vector with the minimum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Min(Vector4<T> value)
        {
            return new Vector4<T>(X.CompareTo(value.X) < 0 ? X : value.X, Y.CompareTo(value.Y) < 0 ? Y : value.Y, Z.CompareTo(value.Z) < 0 ? Z : value.Z, W.CompareTo(value.W) < 0 ? W : value.W);
        }

        /// <summary>
        /// Calculates a vector with the maximum between this vector and another.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Max(Vector4<T> value)
        {
            return new Vector4<T>(X.CompareTo(value.X) > 0 ? X : value.X, Y.CompareTo(value.Y) > 0 ? Y : value.Y, Z.CompareTo(value.Z) > 0 ? Z : value.Z, W.CompareTo(value.W) > 0 ? W : value.W);
        }

        /// <summary>
        /// Clamps this vector between two values.
        /// </summary>
        /// <param name="min">The minimum value.</param>
        /// <param name="max">The maximum value.</param>
        /// <returns>The result of the operation.</returns>
        public Vector4<T> Clamp(Vector4<T> min, Vector4<T> max)
        {
            return Min(max).Max(min);
        }
        
        /// <summary>
        /// Transforms a vector by a matrix.
        /// </summary>
        /// <param name="value">The transformation matrix.</param>
        /// <returns>The result of the operation</returns>
        public Vector4<T> Transform(Matrix4x4<T> value)
        {
            return new Vector4<T>((X.Multiply(value.M11)).Add(Y.Multiply(value.M21)).Add(Z.Multiply(value.M31)).Add(W.Multiply(value.M41)), (X.Multiply(value.M12)).Add(Y.Multiply(value.M22)).Add(Z.Multiply(value.M32)).Add(W.Multiply(value.M42)), (X.Multiply(value.M13)).Add(Y.Multiply(value.M23)).Add(Z.Multiply(value.M33)).Add(W.Multiply(value.M43)), (X.Multiply(value.M14)).Add(Y.Multiply(value.M24)).Add(Z.Multiply(value.M34)).Add(W.Multiply(value.M44)));
        }
        
        /// <summary>
        /// Adds two vectors.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector4<T> operator +(Vector4<T> left, Vector4<T> right)
        {
            return left.Add(right);
        }

        /// <summary>
        /// Subtracts one vector from another.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector4<T> operator -(Vector4<T> left, Vector4<T> right)
        {
            return left.Subtract(right);
        }

        /// <summary>
        /// Multiplies a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector4<T> operator *(Vector4<T> left, T right)
        {
            return left.Multiply(right);
        }

        /// <summary>
        /// Divides a vector by a scalar.
        /// </summary>
        /// <param name="left">The left side of the operation.</param>
        /// <param name="right">The right side of the operation.</param>
        /// <returns>The result of the operation</returns>
        public static Vector4<T> operator /(Vector4<T> left, T right)
        {
            return left.Divide(right);
        }

        /// <summary>
        /// Negates a vector.
        /// </summary>
        /// <param name="value">The value.</param>
        /// <returns>The result of the operation</returns>
        public static Vector4<T> operator -(Vector4<T> value)
        {
            return value;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Vector4<T> left, Vector4<T> right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is not equal to another object of the same type.
        /// </summary>
        /// <param name="left">The left side of the comparison.</param>
        /// <param name="right">The right side of the comparison.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Vector4<T> left, Vector4<T> right)
        {
            return !left.Equals(right);
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Vector4<T> other)
        {
            return X.Equals(other.X) && Y.Equals(other.Y) && Z.Equals(other.Z) && W.Equals(other.W);
        }

        /// <summary>
        /// Determines whether the specified <see cref="System.Object"/> is equal to this instance.
        /// </summary>
        /// <param name="obj">The <see cref="System.Object"/> to compare with this instance.</param>
        /// <returns>
        ///   <c>true</c> if the specified <see cref="System.Object"/> is equal to this instance; otherwise, <c>false</c>.
        /// </returns>
        public override bool Equals(object obj)
        {
            return obj is Vector4<T> ? Equals((Vector4<T>)obj) : false;
        }

        /// <summary>
        /// Returns a hash code for this instance.
        /// </summary>
        /// <returns>
        /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
        /// </returns>
        public override int GetHashCode()
        {
            return X.GetHashCode() ^ Y.GetHashCode() ^ Z.GetHashCode() ^ W.GetHashCode();
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            return "X: " + X.ToString() + ", " + "Y: " + Y.ToString() + ", " + "Z: " + Z.ToString() + ", " + "W: " + W.ToString();
        }
    }
}
